package com.hhd.cwqs.qs.sql;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hhd.cwqs.entity.CwqsDataSource;
import com.hhd.cwqs.utils.CommonUtils;
import org.apache.logging.log4j.util.Strings;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class SingleConnJdbcTemplate extends JdbcTemplate {
    public SingleConnJdbcTemplate() {
    }

    public SingleConnJdbcTemplate(DataSource dataSource) {
        super(dataSource);
    }

    public SingleConnJdbcTemplate(DataSource dataSource, boolean lazyInit) {
        super(dataSource, lazyInit);
    }

    public SingleConnJdbcTemplate(CwqsDataSource dataSource) {
        setDataSource(toDataSource(dataSource));
        setLazyInit(true);
    }

    private static MyDataSource toDataSource(CwqsDataSource dataSource) {
        MyDataSource myDataSource = new MyDataSource();
        myDataSource.setAutoCommit(false);
        myDataSource.setUrl(dataSource.getUrl());
        myDataSource.setUsername(dataSource.getUserName());
        myDataSource.setPassword(dataSource.getPassword());
        myDataSource.setDriverClassName(dataSource.getDriverClass());
        return myDataSource;
    }

    public <T> List<T> queryObjets(String sql, Class<T> elementType) throws DataAccessException {
        return super.query(sql, new BeanPropertyRowMapper<T>(elementType));
    }

    public void rollback() {
        try {
            Objects.requireNonNull(getDataSource()).getConnection().rollback();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void commit() {
        try {
            Objects.requireNonNull(getDataSource()).getConnection().commit();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void close() {
        try {
            getDataSource().getConnection().close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public long saveBatch(List list) {
        if (CommonUtils.isEmpty(list)) return 0;

        int batchSize = 200, size = batchSize;
        long count = 0;
        List<String> batchSql = CommonUtils.newList();
        for (Object o : list) {
            if (CommonUtils.isEmpty(o)) continue;

            batchSql.add(insertSql(o));
            if (size-- <= 0) {
                count += update(joinBatchSql(batchSql));
                size = batchSize;
                batchSql = CommonUtils.newList();
            }
        }
        if (!CommonUtils.isEmpty(batchSql)) {
            count += update(joinBatchSql(batchSql));
        }
        return count;
    }

    private String insertSql(Object o) {
        return String.format("insert into %s (%s) values(%s)",
                getTableName(o), fieldNams(o), values(o));
    }

    private Object values(Object o) {
        Field[] allFields = CommonUtils.getAllFields(o);
        return Strings.join(Arrays.stream(allFields).map(field -> {
            field.setAccessible(true);
            Object value = null;
            try {
                value = field.get(o);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
            if (CommonUtils.isEmpty(value)) return "NULL";
            return "\"" + value.toString() + "\"";
        }).collect(Collectors.toList()), ',');
    }

    private Object fieldNams(Object o) {
        Field[] allFields = CommonUtils.getAllFields(o);
        return Strings.join(Arrays.stream(allFields).map(field -> {
            TableField annotation = CommonUtils.getAnnotation(field, TableField.class);
            if (!CommonUtils.isEmpty(annotation)) return annotation.value();
            return "`" + field.getName() + "`";
        }).collect(Collectors.toList()), ',');
    }

    private String getTableName(Object o) {
        TableName annotation = CommonUtils.getAnnotation(o.getClass(), TableName.class);
        if (!CommonUtils.isEmpty(annotation)) {
            return annotation.value();
        }
        return oConvertUtils.camelToUnderline(o.getClass().getSimpleName());
    }

    private String joinBatchSql(List<String> batchSql) {
        return Strings.join(batchSql, ';');
    }
}